package com.evon.yardmanagement.dao;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

import com.evon.yardmanagement.exception.YardManagementException;
import com.evon.yardmanagement.exception.YardManagementException.ExceptionType;
import com.evon.yardmanagement.model.BaseModel;

/**
 * Data access object (DAO) for domain model
 * 
 * @author MyEclipse Persistence Tools
 */
public abstract class BaseHibernateDAO<T extends BaseModel> implements
		IBaseHibernateDAO {

	private static final Log log = LogFactory.getLog(BaseHibernateDAO.class);

	/**
	 * Each implementing class has to provide their class name (e.g
	 * ClassName.class)
	 * 
	 * @return
	 */
	public abstract Class<T> getQueryClass();

	public Session getSession() {
		return HibernateUtil.getCurrentSession();
	}

	public void save(T transientInstance) {
		log.debug("saving  " + transientInstance + "instance");
		try {
			 Transaction tx = getSession().beginTransaction();
			getSession().save(transientInstance);
			 tx.commit();
			log.debug("save successful");
		} catch (HibernateException re) {
			log.error("save failed", re);
			throw new YardManagementException(re, ExceptionType.RECOVERABLE);
		} catch (RuntimeException rex) {
			log.error("save failed", rex);
			throw new YardManagementException(rex, ExceptionType.RECOVERABLE);
		}
	}

	public void saveOrUpdate(T transientInstance) {
		log.debug("saving  " + transientInstance + "instance");
		try {
			 Transaction tx = getSession().beginTransaction();
			if (transientInstance.getId() > 0) {
				transientInstance = (T) getSession().merge(transientInstance);
				getSession().update(transientInstance);
			} else {
				getSession().save(transientInstance);
			}
			 tx.commit();
			log.debug("save successful");
		} catch (HibernateException re) {
			log.error("save failed", re);
			throw new YardManagementException(re, ExceptionType.RECOVERABLE);
		} catch (RuntimeException rex) {
			log.error("save failed", rex);
			throw new YardManagementException(rex, ExceptionType.RECOVERABLE);
		}
	}

	public void delete(T persistentInstance) {
		log.debug("deleting" + persistentInstance + " instance");
		try {
			Transaction tx = getSession().beginTransaction();
			getSession().delete(persistentInstance);
			tx.commit();
			log.debug("delete successful");
		} catch (HibernateException re) {
			log.error("delete failed", re);
			throw new YardManagementException(re, ExceptionType.RECOVERABLE);
		} catch (RuntimeException rex) {
			log.error("delete failed", rex);
			throw new YardManagementException(rex, ExceptionType.RECOVERABLE);
		}
	}

	public T findById(java.lang.Integer id) {
		log.debug("getting  instance with id: " + id);
		try {
			Criteria criteria = getSession().createCriteria(getQueryClass());
			criteria.add(Restrictions.eq("id", id));
			T instance = (T) criteria.uniqueResult();
			return instance;
		} catch (HibernateException re) {
			log.error("get failed", re);
			throw new YardManagementException(re, ExceptionType.RECOVERABLE);
		} catch (RuntimeException rex) {
			log.error("get failed", rex);
			throw new YardManagementException(rex, ExceptionType.RECOVERABLE);
		}
	}

	public List<T> findByProperty(String propertyName, Object value) {
		log.debug("finding  instance with property: " + propertyName
				+ ", value: " + value);
		try {
			Criteria criteria = getSession().createCriteria(getQueryClass());
			criteria.add(Restrictions.eq(propertyName, value));

			return criteria.list();
		} catch (HibernateException re) {
			log.error("find by property name failed", re);
			throw new YardManagementException(re, ExceptionType.RECOVERABLE);
		} catch (RuntimeException rex) {
			log.error("find by property name failed", rex);
			throw new YardManagementException(rex, ExceptionType.RECOVERABLE);
		}
	
	}

	public List<T> findAll() {
		log.debug("finding all  instances");
		try {
			return getSession().createCriteria(getQueryClass()).list();
		} catch (HibernateException re) {
			log.error("find all failed", re);
			throw new YardManagementException(re, ExceptionType.RECOVERABLE);
		} catch (RuntimeException rex) {
			log.error("find all failed", rex);
			throw new YardManagementException(rex, ExceptionType.RECOVERABLE);
		}
	
	}

	public T merge(T detachedInstance) {
		log.debug("merging  instance");
		try {
			T result = (T) getSession().merge(detachedInstance);
			log.debug("merge successful");
			return result;
		} catch (RuntimeException re) {
			log.error("merge failed", re);
			throw re;
		}
	}

	public void attachDirty(T instance) {
		log.debug("attaching dirty  instance");
		try {
			getSession().saveOrUpdate(instance);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public void attachClean(T instance) {
		log.debug("attaching clean  instance");
		try {
			getSession().lock(instance, LockMode.NONE);
			log.debug("attach successful");
		} catch (RuntimeException re) {
			log.error("attach failed", re);
			throw re;
		}
	}

	public void update(T transientInstance) {
		log.debug("updating  " + transientInstance + "instance");
		try {
			 Transaction tx = getSession().beginTransaction();
			getSession().update(transientInstance);
			tx.commit();
			log.debug("save successful");
		} catch (HibernateException re) {
			log.error("save failed", re);
			throw new YardManagementException(re, ExceptionType.RECOVERABLE);
		} catch (RuntimeException rex) {
			log.error("save failed", rex);
			throw new YardManagementException(rex, ExceptionType.RECOVERABLE);
		}
	}

}